import pyaudio
import numpy as np
import time
import winsound
import threading
import tkinter as tk
from tkinter import ttk


# ==================== 分贝计算函数 ====================
def get_db():
    db_lst = []
    p = pyaudio.PyAudio()
    stream = p.open(format=pyaudio.paInt16,
                    channels=2,
                    rate=44100,
                    input=True,
                    frames_per_buffer=1024)
    for i in range(60):
        data = stream.read(1024)
        audio_data = np.frombuffer(data, dtype=np.short)
        temp = np.max(audio_data)
        rms = np.sqrt(np.mean(temp**2))
        db = int(20 * np.log10(rms))
        db_lst.append(db)
    stream.stop_stream()
    stream.close()
    p.terminate()
    for i in range(4):
        db_lst.remove(max(db_lst))
    for i in range(4):
        db_lst.remove(min(db_lst))
    db_mean = int(sum(db_lst) / len(db_lst))
    return db_mean
# =======================================================================


# ==================== 蜂鸣监控函数 ====================
stop_flag = [False]

def dididi(yuzhi_db, duration=5):
    p = pyaudio.PyAudio()
    try:
        stream = p.open(format=pyaudio.paInt16,
                        channels=2,
                        rate=44100,
                        input=True,
                        frames_per_buffer=1024)

        start_time = None  # 记录开始低于阈值的时间

        while not stop_flag[0]:
            data = stream.read(1024)
            audio_data = np.frombuffer(data, dtype=np.short)
            temp = np.max(audio_data)
            rms = np.sqrt(np.mean(temp**2))
            db = int(20 * np.log10(rms))

            if db >= yuzhi_db:
                start_time = None  # 重置计时
            else:
                if start_time is None:
                    start_time = time.time()  # 开始计时
                elif time.time() - start_time >= duration:
                    stream.stop_stream()
                    stream.close()
                    p.terminate()
                    winsound.Beep(1000, 1000)
                    return  # 结束监控
            time.sleep(0.5)
    except Exception as e:
        print("监控出错:", e)
    finally:
        try:
            stream.stop_stream()
            stream.close()
            p.terminate()
        except:
            pass
# =======================================================================


# ==================== GUI 界面 ====================
def create_gui():
    root = tk.Tk()
    root.title("🔊 音频分贝监控器")
    root.geometry("500x400")
    root.resizable(False, False)
    root.configure(bg="#f0f0f0")

    # 变量
    current_db_var = tk.StringVar(value="当前分贝: -- dB")
    threshold_db_var = tk.IntVar(value=30)
    duration_var = tk.IntVar(value=5)
    runtime_var = tk.StringVar(value="运行时间: 0 秒")
    monitoring = tk.BooleanVar(value=False)  # 是否正在监控

    # 主框架
    frame = tk.Frame(root, bg="#f0f0f0", padx=20, pady=20)
    frame.pack(expand=True, fill="both")

    # 标题
    tk.Label(frame, text="音频分贝监控器", font=("微软雅黑", 16, "bold"), bg="#f0f0f0", fg="#333").grid(row=0, column=0, columnspan=2, pady=(0, 20))

    # 当前分贝显示
    tk.Label(frame, textvariable=current_db_var, font=("Consolas", 14), bg="#f0f0f0", fg="blue").grid(row=1, column=0, columnspan=2, pady=10)

    # 获取当前分贝按钮
    def on_get_db():
        try:
            db = get_db()
            current_db_var.set(f"当前分贝: {db} dB")
        except Exception as e:
            current_db_var.set("获取失败")
            print("获取分贝失败:", e)

    ttk.Button(frame, text="🔍 获取当前分贝值", command=on_get_db).grid(row=2, column=0, columnspan=2, pady=10, ipadx=10, ipady=5)

    # 阈值设置
    tk.Label(frame, text="分贝阈值:", bg="#f0f0f0").grid(row=3, column=0, sticky="w", pady=5)
    threshold_spin = ttk.Spinbox(frame, from_=0, to=100, textvariable=threshold_db_var, width=10)
    threshold_spin.grid(row=3, column=1, sticky="w", padx=10)

    # 时间阈值设置
    tk.Label(frame, text="时间阈值 (秒):", bg="#f0f0f0").grid(row=4, column=0, sticky="w", pady=5)
    duration_spin = ttk.Spinbox(frame, from_=1, to=30, textvariable=duration_var, width=10)
    duration_spin.grid(row=4, column=1, sticky="w", padx=10)

    # 运行时间显示
    tk.Label(frame, textvariable=runtime_var, font=("Arial", 10), bg="#f0f0f0", fg="gray").grid(row=5, column=0, columnspan=2, pady=10)

    # 开始/停止监控按钮
    control_btn = ttk.Button(frame, text="▶️ 开始监控")

    def start_monitoring():
        if monitoring.get():
            return
        monitoring.set(True)
        stop_flag[0] = False
        yuzhi = threshold_db_var.get()
        dur = duration_var.get()
        control_btn.config(text="⏹️ 停止监控")

        # 启动运行时间计时线程
        def runtime_counter():
            start_time = time.time()
            while monitoring.get() and not stop_flag[0]:
                elapsed = int(time.time() - start_time)
                runtime_var.set(f"运行时间: {elapsed} 秒")
                time.sleep(1)
            runtime_var.set("运行时间: 0 秒")

        counter_thread = threading.Thread(target=runtime_counter, daemon=True)
        counter_thread.start()

        # 启动监控线程
        def monitor():
            dididi(yuzhi, dur)
            monitoring.set(False)
            stop_flag[0] = True
            control_btn.config(text="▶️ 开始监控")

        thread = threading.Thread(target=monitor, daemon=True)
        thread.start()

    def toggle_monitoring():
        if monitoring.get():
            stop_flag[0] = True
            monitoring.set(False)
            control_btn.config(text="▶️ 开始监控")
        else:
            start_monitoring()

    control_btn.config(command=toggle_monitoring)
    control_btn.grid(row=6, column=0, columnspan=2, pady=20, ipadx=20, ipady=8)

    # 状态栏提示
    tk.Label(frame, text="提示：当音量持续低于阈值时触发蜂鸣", bg="#f0f0f0", fg="green", font=("Arial", 9)).grid(row=7, column=0, columnspan=2, pady=(10, 0))

    # 窗口关闭事件
    def on_closing():
        stop_flag[0] = True
        root.destroy()

    root.protocol("WM_DELETE_WINDOW", on_closing)

    root.mainloop()


# 启动程序
if __name__ == "__main__":
    create_gui()
